//-----------------------------------------------------------------------------
// File: Graphics.cs
//
// Copyright (c) Dmitry Shuklin. All rights reserved.
//-----------------------------------------------------------------------------
using System;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace Cerebrum.Direct3D.Drawing
{
	/// <summary>
	/// Summary description for Graphics.
	/// </summary>
	public abstract class Graphics : System.IDisposable
	{
		protected Microsoft.DirectX.Direct3D.Device m_Device;

		protected Graphics(Microsoft.DirectX.Direct3D.Device device)
		{
			this.m_Device = device;
			this.m_Device.DeviceReset += new System.EventHandler(this.RestoreDeviceObjects);
		}

		protected static object g_FieldTransformedGraphicsObject = new object();
		protected static object g_FieldPositionGraphicsObject = new object();

		public static Graphics FromDevice(Microsoft.DirectX.Direct3D.Device device)
		{
			return FromDevice(device, GraphicsVertexType.Transformed);
		}

		public static Graphics FromDevice(Microsoft.DirectX.Direct3D.Device device, GraphicsVertexType vertexType)
		{
			Graphics _Graphics = null;
			switch(vertexType)
			{
				case GraphicsVertexType.Transformed:
				{
					_Graphics = DeviceObjects[device, g_FieldTransformedGraphicsObject] as Graphics;
					if(_Graphics==null)
					{
						_Graphics = new TransformedGraphics(device);
						DeviceObjects[device, g_FieldTransformedGraphicsObject] = _Graphics;
					}
					break;
				}
				case GraphicsVertexType.Position:
				{
					_Graphics = DeviceObjects[device, g_FieldPositionGraphicsObject] as Graphics;
					if(_Graphics==null)
					{
						_Graphics = new PositionGraphics(device);
						DeviceObjects[device, g_FieldPositionGraphicsObject] = _Graphics;
					}
					break;
				}
			}
			return _Graphics;
		}

		
		protected static VertexBuffer GetVertexBuffer(Microsoft.DirectX.Direct3D.Device device, System.Type vertexType, int length, Microsoft.DirectX.Direct3D.VertexFormats format)
		{
			string key = string.Intern(vertexType.Name + "." + length.ToString());

			VertexBuffer _VertexBuffer = DeviceObjects[device, key] as VertexBuffer;
			if(_VertexBuffer==null)
			{
				_VertexBuffer = new VertexBuffer(vertexType, length, device, Usage.WriteOnly, format, Pool.SystemMemory);
				DeviceObjects[device, key] = _VertexBuffer;
			}
			return _VertexBuffer;
		}

		public abstract void DrawRectangle(System.Drawing.RectangleF rectangle, float z, System.Drawing.Color color, bool fill);

		public abstract void DrawRectangle(System.Drawing.RectangleF rectangle, float backZ, System.Drawing.Color backColor, float foreZ, System.Drawing.Color foreColor);

		public void DrawRectangle(System.Drawing.RectangleF rectangle, float z, Microsoft.DirectX.Direct3D.Texture texture, System.Drawing.Color color)
		{
			this.DrawRectangle(rectangle, z, texture, new System.Drawing.RectangleF(0,0,1,1), color);
		}
		public abstract void DrawRectangle(System.Drawing.RectangleF rectangle, float z, Microsoft.DirectX.Direct3D.Texture texture, System.Drawing.RectangleF source, System.Drawing.Color color);
	

		public abstract void DrawEllipse(System.Drawing.RectangleF rectangle, float z, System.Drawing.Color color, int count, bool fill);

		public abstract void DrawLine(System.Drawing.PointF p1, System.Drawing.PointF p2, float z, System.Drawing.Color c1, System.Drawing.Color c2);

		/// <summary>
		/// Restore the objects after a device has been reset
		/// </summary>
		public virtual void RestoreDeviceObjects(object sender, EventArgs e)
		{
		}

		public static Viewport ViewportFromRectangle(System.Drawing.RectangleF rect, float minZ, float maxZ)
		{
			Viewport viewport = new Viewport();
			viewport.X = (int)(Math.Floor(rect.X) + 0.5);
			viewport.Y = (int)(Math.Floor(rect.Y) + 0.5);
			viewport.Width = (int)(Math.Floor(rect.Width + 0.5) + 0.5);
			viewport.Height = (int)(Math.Floor(rect.Height + 0.5) + 0.5);
			viewport.MinZ = minZ;
			viewport.MaxZ = maxZ;
			return viewport;
		}


		protected static DeviceObjectsCollection m_DeviceObjects;

		public static DeviceObjectsCollection DeviceObjects
		{
			get
			{
				if(m_DeviceObjects == null)
				{
					m_DeviceObjects = new DeviceObjectsCollection();
				}
				return m_DeviceObjects;
			}
		}

		public class DeviceObjectsCollection
		{
			private System.Collections.Hashtable m_Devices;
			internal DeviceObjectsCollection()
			{
				m_Devices = new System.Collections.Hashtable();
			}
			public object this[Device device, object key]
			{
				get
				{
					System.Collections.Hashtable _objhash = null;
					if(m_Devices.ContainsKey(device))
					{
						_objhash = m_Devices[device] as System.Collections.Hashtable;
					}
					if(_objhash != null)
					{
						return _objhash[key];
					}
					return null;
				}
				set
				{
					System.Collections.Hashtable _objhash = null;
					if(m_Devices.ContainsKey(device))
					{
						_objhash = m_Devices[device] as System.Collections.Hashtable;
					}
					if(_objhash == null)
					{
						_objhash = new System.Collections.Hashtable();
						device.DeviceLost += new EventHandler(device_DeviceLost);
						device.Disposing +=new EventHandler(device_Disposing);
						m_Devices.Add(device, _objhash);
					}
					if(_objhash != null)
					{
						if(value!=null)
						{
							_objhash[key] = value;
						}
						else if(_objhash.ContainsKey(key))
						{
							_objhash.Remove(key);
						}
					}
				}
			}
			
			public void DisposeObject(object key)
			{
				foreach(Device device in m_Devices.Keys)
				{
					DisposeObject(device, key);
				}
			}

			public void DisposeObject(Device device, object key)
			{
				object item = this[device, key];
				if(item!=null)
				{
					this[device, key] = null;
					InternalDisposeObject(this, item, true);
				}
			}

			private bool InternalDisposeObject(object sender, object item, bool force)
			{
				Microsoft.DirectX.Direct3D.Font _font = item as Microsoft.DirectX.Direct3D.Font;
				if(_font != null)
				{
					if(force)
					{
						_font.Dispose();
						return true;
					}
					return false;
				}
				IndexBuffer _ibuf = item as IndexBuffer;
				if(_ibuf != null)
				{
					_ibuf.Dispose();
					return true;
				}
				VertexBuffer _vbuf = item as VertexBuffer;
				if(_vbuf != null)
				{
					_vbuf.Dispose();
					return true;
				}
				Texture _txtr = item as Texture;
				if(_txtr != null)
				{
					_txtr.Dispose();
					return true;
				}
				/*Cerebrum.IReleasable _rlxl = item as Cerebrum.IReleasable;
				if(_rlxl != null)
				{
					_rlxl.Clean(sender, System.EventArgs.Empty);
					return true;
				}*/
				System.IDisposable _idsp = item as System.IDisposable;
				if(_idsp != null)
				{
					_idsp.Dispose();
					return true;
				}
				return true;
			}

			private void device_DeviceLost(object sender, EventArgs e)
			{
				System.Collections.Hashtable _objhash = null;
				if(m_Devices.ContainsKey(sender))
				{
					_objhash = m_Devices[sender] as System.Collections.Hashtable;
					InternalReleaseObjects(sender, _objhash, false);
				}
			}

			private void device_Disposing(object sender, EventArgs e)
			{
				Device device = sender as Device;
				if(device!=null)
				{
					device.DeviceLost -= new EventHandler(device_DeviceLost);
					device.Disposing -=new EventHandler(device_Disposing);
				}

				System.Collections.Hashtable _objhash = null;
				if(m_Devices.ContainsKey(sender))
				{
					_objhash = m_Devices[sender] as System.Collections.Hashtable;
					m_Devices.Remove(sender);
					InternalReleaseObjects(sender, _objhash, true);
				}
			}

			private void InternalReleaseObjects(object sender, System.Collections.Hashtable _objhash, bool force)
			{
				if(_objhash != null)
				{
					object [] objs = new object[_objhash.Count];
					_objhash.CopyTo(objs, 0);
					foreach(System.Collections.DictionaryEntry entry in objs)
					{
						if(InternalDisposeObject(sender, entry.Value, force))
						{
							_objhash.Remove(entry.Key);
						}
					}
					if(force) _objhash.Clear();
				}
			}
		}
		#region IDisposable Members

		public void Dispose()
		{
			this.Dispose(true);
		}


		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected virtual void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(this.m_Device!=null)
				{
					this.m_Device.DeviceReset -= new System.EventHandler(this.RestoreDeviceObjects);
					this.m_Device = null;
				}
			}
		}

		#endregion
	}

	public enum ImageRenderMode
	{
		CenterImage,
		StretchImage
	}

	public enum GraphicsVertexType
	{
		Transformed,
		Position
	}

	[Flags]
	public enum DestroyObjectFlags
	{
		Auto			= 0,
		OnLostDevice	= 1,
		OnDisposeDevice	= 2
	}
}
